package com.hjk.common.aop.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hjk.common.utils.aop.AopUtils;
import com.hjk.common.utils.check.Check;
import com.hjk.common.utils.concurrency.FastTaskUtils;
import com.hjk.common.utils.string.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;


@Aspect
@Component
@Slf4j
public class WebLogAspect {

    private static final String JSON_PARAM = "json_param";

    @Pointcut("execution( * com.hjk..*.*Controller.*(..))")
    public void webLog() {

    }

    @Around("webLog()")
    public Object webLog(ProceedingJoinPoint point) throws Throwable {
        //先执行业务
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 只记录post方法
        if (MediaType.APPLICATION_JSON_VALUE.equals(request.getContentType()) && "POST".equals(request.getMethod())) {
            // 记录下请求内容
            // 获取参数, 只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管
            if (point.getArgs().length > 0) {
                for (Object o : point.getArgs()) {
                    if (o instanceof HttpServletRequest || o instanceof HttpServletResponse) {
                        continue;
                    }
                    request.setAttribute(JSON_PARAM, o);
                }
            }
        }
        String time = "";
        String url = request.getRequestURL().toString();
        Object result = null;
        try {
            long start = System.nanoTime();
            result = point.proceed();
            long end = System.nanoTime();
            time = new BigDecimal((end - start) / 1000_000_000.0).setScale(6, RoundingMode.UP).toString();
            log.info("{} run time :  {} s", url, time);
            return result;
        } finally {
            try {
                // 判断控制器方法参数中是否有RequestBody注解
                // 获取真实的方法对象
                MethodSignature methodSignature = (MethodSignature) point.getSignature();
                boolean isRequestBody = isRequestBody(methodSignature.getMethod().getParameterAnnotations());

                Object requestParamObject = getRequestParamObject(point, request, isRequestBody);

                // 记录下请求内容
                log.info("URL: {}  IP: {}\n requestParam:\n{}",
                        //AopUtils.getMethodName(point),
                        request.getRequestURL().toString(),
                        request.getRemoteAddr(),
                        JsonUtils.jsonFormat(requestParamObject));

            } catch (Exception e) {
                log.error("打印日志错误: ", e);
            }
        }

    }

    /**
     * 获取请求参数JSON字符串
     */
    protected Object getRequestParamObject(ProceedingJoinPoint joinPoint, HttpServletRequest request, boolean isRequestBody) {
        Object paramObject = null;
        if (isRequestBody) {
            // POST,application/json,RequestBody的类型,简单判断,然后序列化成JSON字符串
            Object[] args = joinPoint.getArgs();
            paramObject = getArgsObject(args);
        } else {
            // 获取getParameterMap中所有的值,处理后序列化成JSON字符串
            Map<String, String[]> paramsMap = request.getParameterMap();
            paramObject = getParamJSONObject(paramsMap);
        }
        return paramObject;
    }

    /**
     * 请求参数拼装
     *
     * @param args
     * @return
     */
    protected Object getArgsObject(Object[] args) {
        if (args == null) {
            return null;
        }
        // 去掉HttpServletRequest和HttpServletResponse
        List<Object> realArgs = new ArrayList<>();
        for (Object arg : args) {
            if (arg instanceof HttpServletRequest) {
                continue;
            }
            if (arg instanceof HttpServletResponse) {
                continue;
            }
            if (arg instanceof MultipartFile) {
                continue;
            }
            if (arg instanceof ModelAndView) {
                continue;
            }
            realArgs.add(arg);
        }
        if (realArgs.size() == 1) {
            return realArgs.get(0);
        } else {
            return realArgs;
        }
    }

    /**
     * 获取参数Map的JSON字符串
     *
     * @param paramsMap
     * @return
     */
    protected JSONObject getParamJSONObject(Map<String, String[]> paramsMap) {
        if (ObjectUtils.isEmpty(paramsMap)) {
            return null;
        }
        JSONObject jsonObject = new JSONObject();
        for (Map.Entry<String, String[]> kv : paramsMap.entrySet()) {
            String key = kv.getKey();
            String[] values = kv.getValue();
            // 没有值
            if (values == null) {
                jsonObject.put(key, null);
            } else if (values.length == 1) {
                // 一个值
                jsonObject.put(key, values[0]);
            } else {
                // 多个值
                jsonObject.put(key, values);
            }
        }
        return jsonObject;
    }

    /**
     * 判断控制器方法参数中是否有RequestBody注解
     *
     * @param annotations
     * @return
     */
    protected boolean isRequestBody(Annotation[][] annotations) {
        boolean isRequestBody = false;
        for (Annotation[] annotationArray : annotations) {
            for (Annotation annotation : annotationArray) {
                if (annotation instanceof RequestBody) {
                    isRequestBody = true;
                }
            }
        }
        return isRequestBody;
    }


}
